import os
import time
import platform
import psutil
import logging
from datetime import datetime
from flask import current_app

# 配置日志
logging.basicConfig(level=logging.INFO, 
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger('system_monitor')

# 全局变量
_system_info_cache = {}
system_stats = {
    'cpu_percent': 0,
    'memory_used': 0,
    'memory_total': 0,
    'memory_percent': 0,
    'disk_used': 0,
    'disk_total': 0,
    'disk_percent': 0,
    'temperature': 0,
    'loadavg': [0, 0, 0],
    'uptime': 0
}

# 全局调度器
scheduler = None

def get_box_id():
    """获取设备唯一ID"""
    try:
        # 首先尝试从配置中获取
        if current_app.config.get('BOX_ID'):
            return current_app.config.get('BOX_ID')
        
        # 获取MAC地址作为基础
        mac = get_mac_address().replace(':', '')
        
        # 生成设备ID
        box_id = f"MOBOX-{mac[:4]}-{mac[4:8]}-{mac[8:12]}"
        return box_id
    except Exception as e:
        logger.error(f"获取设备ID失败: {str(e)}")
        return "UNKNOWN"

def get_mac_address():
    """获取主网卡MAC地址"""
    try:
        for interface, addrs in psutil.net_if_addrs().items():
            if interface.startswith(('eth', 'en', 'wlan', 'wlp')):
                for addr in addrs:
                    if addr.family == psutil.AF_LINK:
                        return addr.address
        
        # 如果找不到符合条件的网卡，返回第一个找到的MAC地址
        for interface, addrs in psutil.net_if_addrs().items():
            for addr in addrs:
                if addr.family == psutil.AF_LINK:
                    return addr.address
                    
        return "00:00:00:00:00:00"
    except Exception as e:
        logger.error(f"获取MAC地址失败: {str(e)}")
        return "00:00:00:00:00:00"

def update_system_stats():
    """更新系统状态信息"""
    global system_stats
    
    try:
        # CPU使用率
        system_stats['cpu_percent'] = psutil.cpu_percent(interval=0.1)
        
        # 内存信息
        memory = psutil.virtual_memory()
        system_stats['memory_used'] = memory.used
        system_stats['memory_total'] = memory.total
        system_stats['memory_percent'] = memory.percent
        
        # 磁盘信息
        disk = psutil.disk_usage('/')
        system_stats['disk_used'] = disk.used
        system_stats['disk_total'] = disk.total
        system_stats['disk_percent'] = disk.percent
        
        # 系统负载
        system_stats['loadavg'] = os.getloadavg()
        
        # CPU温度
        try:
            # 尝试获取温度信息
            if os.path.exists('/sys/class/thermal/thermal_zone0/temp'):
                with open('/sys/class/thermal/thermal_zone0/temp', 'r') as f:
                    temp = float(f.read().strip()) / 1000.0
                    system_stats['temperature'] = temp
        except Exception as e:
            # 温度读取失败时不影响其他状态信息
            logger.debug(f"读取温度失败: {str(e)}")
        
        # 系统运行时间
        system_stats['uptime'] = time.time() - psutil.boot_time()
        
        return system_stats.copy()  # 返回副本避免并发修改
    except Exception as e:
        logger.error(f"更新系统状态失败: {str(e)}")
        return system_stats.copy()

def get_system_info():
    """获取基本系统信息"""
    try:
        info = {
            'hostname': 'mobox',  # 固定显示为 mobox
            'os': f"{platform.system()} {platform.release()}",
            'kernel': platform.version(),
            'arch': platform.machine(),
            'machine': platform.platform(),
            'version': platform.version(),
            'box_id': get_box_id(),
            'mac_address': get_mac_address(),
            'uptime': time.time() - psutil.boot_time(),
            'ip_address': _get_host_ip(),
        }
        return info
    except Exception as e:
        logger.error(f"获取系统信息失败: {str(e)}")
        return {}

def get_system_status():
    """获取系统实时状态"""
    # 这个函数现在作为 update_system_stats 的别名，确保一致性
    return update_system_stats()

def get_system_metrics():
    """获取详细的系统性能指标 - 此函数现在可以被 get_system_status 替代或扩展"""
    # 为了保持API的兼容性，我们可以让它返回与 status 相同的数据
    # 或者返回一个更详细的超集
    try:
        status_data = get_system_status()
        
        # 内存详细信息
        mem = psutil.virtual_memory()
        swap = psutil.swap_memory()
        
        # CPU详细信息
        cpu_times = psutil.cpu_times_percent()
        
        # 网络IO
        net_io = psutil.net_io_counters()
        
        metrics = {
            'status': status_data, # 包含所有基本状态
            'cpu': {
                'count': psutil.cpu_count(),
                'physical_count': psutil.cpu_count(logical=False),
                'percent_per_cpu': psutil.cpu_percent(interval=0.1, percpu=True),
                'times_percent': {
                    'user': cpu_times.user,
                    'system': cpu_times.system,
                    'idle': cpu_times.idle,
                    'iowait': getattr(cpu_times, 'iowait', 0) # iowait在非Linux系统上可能不存在
                }
            },
            'memory': {
                'total': mem.total,
                'available': mem.available,
                'used': mem.used,
                'percent': mem.percent
            },
            'swap': {
                'total': swap.total,
                'used': swap.used,
                'free': swap.free,
                'percent': swap.percent
            },
            'disk': {
                'io_counters': {k: v._asdict() for k, v in psutil.disk_io_counters(perdisk=True).items()}
            },
            'network': {
                'bytes_sent': net_io.bytes_sent,
                'bytes_recv': net_io.bytes_recv,
                'packets_sent': net_io.packets_sent,
                'packets_recv': net_io.packets_recv,
            }
        }
        return metrics
    except Exception as e:
        logger.error(f"获取系统指标失败: {str(e)}")
        return {}

def get_process_list(sort_by='cpu_percent', limit=50):
    """获取进程列表"""
    processes = []
    try:
        for p in psutil.process_iter(['pid', 'name', 'username', 'cpu_percent', 'memory_percent', 'create_time', 'status']):
            try:
                processes.append(p.info)
            except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
                pass
        
        # 排序
        if sort_by in ['cpu_percent', 'memory_percent']:
            processes.sort(key=lambda x: x[sort_by], reverse=True)
        
        return processes[:limit]
    except Exception as e:
        logger.error(f"获取进程列表失败: {str(e)}")
        return []

def broadcast_status():
    """广播系统状态到所有连接的客户端"""
    try:
        status = get_system_status()
        # 注意：不要直接导入socketio，而是从应用上下文中获取
        if hasattr(current_app, 'extensions') and 'socketio' in current_app.extensions:
            from flask_socketio import emit
            emit('system_status', status, namespace='/', broadcast=True)
        return status
    except Exception as e:
        logger.error(f"广播系统状态失败: {str(e)}")
        return {'error': str(e)}

def init_system_monitor(app):
    """初始化系统监控"""
    global scheduler
    
    # 只有调度器为None时才创建新的
    if scheduler is None:
        try:
            # 导入调度器
            from apscheduler.schedulers.background import BackgroundScheduler
            
            # 创建新调度器
            scheduler = BackgroundScheduler()
            
            # 更新一次系统信息
            with app.app_context():
                update_system_stats()
        
            # 定义应用上下文包装器
            def with_app_context(func):
                def wrapper():
                    try:
                        with app.app_context():
                            return func()
                    except Exception as e:
                        logger.error(f"任务执行失败: {str(e)}")
                        return None
                return wrapper
            
            # 添加定时任务
            scheduler.add_job(
                with_app_context(update_system_stats),
                'interval',
                seconds=5,
                id='update_system_stats',
                replace_existing=True
            )
            
            # 添加广播任务
            scheduler.add_job(
                with_app_context(broadcast_status),
                'interval',
                seconds=10,
                id='broadcast_status',
                replace_existing=True
            )
        
            # 启动调度器
            if not scheduler.running:
                scheduler.start()
                logger.info("系统监控服务已启动")
        
            # 确保进程退出时关闭调度器
            import atexit
            
            def shutdown_scheduler():
                global scheduler
                if scheduler and scheduler.running:
                    try:
                        scheduler.shutdown(wait=False)
                        logger.info("系统监控服务已安全关闭")
                    except Exception as e:
                        logger.error(f"关闭系统监控调度器失败: {str(e)}")
            
            atexit.register(shutdown_scheduler)
            
            return scheduler
        
        except Exception as e:
            logger.error(f"初始化系统监控失败: {str(e)}")
            return None
    else:
        logger.info("系统监控服务已存在，跳过初始化")
        return scheduler


def shutdown_system_monitor():
    """关闭系统监控服务"""
    global scheduler
    if scheduler and scheduler.running:
        try:
            scheduler.shutdown(wait=False)
            logger.info("系统监控服务已手动关闭")
            scheduler = None
        except Exception as e:
            logger.error(f"关闭系统监控调度器失败: {str(e)}")
    else:
        logger.info("系统监控服务未运行或已关闭") 

# 内部工具函数：获取外网ip或本地ip
def _get_host_ip():
    """尝试获取本机IPv4地址

    首先尝试通过 UDP 套接字连接公共 DNS 服务器获取本地地址，
    如果失败则回退为 127.0.0.1。"""
    import socket
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("8.8.8.8", 80))
        ip = s.getsockname()[0]
        s.close()
        return ip
    except Exception:
        return "127.0.0.1" 